home *** CD-ROM | disk | FTP | other *** search
/ Aminet 21 / Aminet 21 (1997)(GTI - Schatztruhe)[!][Oct 1997].iso / Aminet / comm / bbs / cit_src_7H21.lha / misc.c < prev    next >
C/C++ Source or Header  |  1997-07-27  |  52KB  |  1,957 lines

  1. /*
  2. *       misc.c
  3. *
  4. * Random functions.
  5. */
  6. /*
  7. *       history
  8. *
  9. * 86Aug19 HAW  Kill history because of space problems.
  10. * 84Jun10 JLS  Function changedate() installed.
  11. * 84May01 HAW  Starting 1.50a upgrade.
  12. * 83Mar12 CrT  from msg.c
  13. * 83Mar03 CrT & SB   Various bug fixes...
  14. * 83Feb27 CrT  Save private mail for sender as well as recipient.
  15. * 83Feb23 Various.  transmitFile() won't drop first char on WC...
  16. * 82Dec06 CrT  2.00 release.
  17. * 82Nov05 CrT  Stream retrieval.  Handles messages longer than MAXTEXT.
  18. * 82Nov04 CrT  Revised disk format implemented.
  19. * 82Nov03 CrT  Individual history begun.  General cleanup.
  20. */
  21. #include "ctdl.h"
  22. #include <dos.h>
  23. #define  TURBO_C_VSPRINTF_BUG
  24. /*
  25. *       contents
  26. *
  27. * ARCDir()    ARC TOC entries
  28. * calcrc()    calculates CRC
  29. * changeDate()      allow changing of date
  30. * CheckDLimit()     exceeded download time limit?
  31. * civTime()   MilTime to CivTime
  32. * CompressedDir()   manager of reading compressed dirs
  33. * configure()   sets terminal parameters via dialogue
  34. * crashout()    crashes out of Citadel in case of bug
  35. * doFormatted()   for wildCard
  36. * doCR()      newline on modem and console
  37. * download()    menu-level routine for WC-protocol sends
  38. * formRoom()    room prompt formatting
  39. * getCdate()    gets date from system clock.
  40. * GetSecond()   get seconds of minute, for multibanner
  41. * GifDir()    important data of a GIF file.
  42. * HelpIfPresent()   print help file if present
  43. * ingestFile()    puts file in held message buffer
  44. * lbyte()     finds 0 byte of a string
  45. * patchDebug()    display/patch byte
  46. * printDate()   prints out date
  47. * putBufChar()    .EWM/.EXM/.EWN/.EXN internal
  48. * putFLChar()   readFile() -> disk file interface
  49. * reconfigure()   Reconfigures a user
  50. * TranFiles()   Handles file transfers to users
  51. * TranSend()    Does send work of TranFiles()
  52. * transmitFile()    send a host file, no formatting
  53. * tutorial()    first level for printing a help file
  54. * upLoad()    menu-level read-via-WC-protocol fn
  55. * visible()   convert control chars to letters
  56. * writeTutorial()   prints a .hlp file
  57. * ZIPDir()    ZIP TOC entries
  58. */
  59.  
  60. static void Display_User_Configuration(void);
  61.  
  62.  
  63. extern int ClassActive[EVENT_CLASS_COUNT]; /* which classes are active? */
  64. char  *monthTab[13] =
  65.   {
  66.   "", "Jan", "Feb", "Mar",
  67.   "Apr", "May", "Jun",
  68.   "Jul", "Aug", "Sep",
  69.   "Oct", "Nov", "Dec"
  70.  
  71.   };
  72. FILE  *upfd;
  73. int masterCount;
  74. int acount;
  75. long byteRate; /* Bytes/sec that modem is set for.     */
  76. int DirAlign = 0;
  77. long  LowFree;
  78. char  AlignChar;
  79. char  *NoFileStr = "\n No %s.\n";
  80. char  *who_str = "who";
  81. /* char   *VERSION = "3.42.s6"; */
  82. char  *VERSION = VERSION_NAME;
  83. char  *ALL_LOCALS  = "&L";
  84. char  *R_SH_MARK   = "&&";
  85. char  *LOC_NET     = "++";
  86. char  *NON_LOC_NET = "%%";
  87. char  *WRITE_LOCALS = "All Local Systems";
  88. char  *APrivateRoom = "A Private Room";
  89. char  *LCHeld = "log%d.hld";
  90. char  FormatFlag = FALSE;
  91. long  Dl_Limit = -1l;
  92. long  *DL_Total;   /* Blech */
  93. extern SListBase MailForward;
  94. extern char FileTransStat;
  95. PROTO_TABLE Table[] =
  96.   {
  97.     {
  98.     "Ascii", 0, (IS_NUMEROUS | NEEDS_HDR), "ASCII", NULL, NULL, NULL,
  99.     (int (*)(int))outMod, 1, AsciiHeader, NULL
  100.  
  101.     }
  102.   ,
  103.     {
  104.     "Xmodem", 13, (RIGAMAROLE | IS_DL), "Xmodem", "Xmodem",
  105.     "wcdown.blb", "wcupload.blb", sendWCChar, SECTSIZE, NULL,
  106.     XYClear
  107.  
  108.     }
  109.   ,
  110.     {
  111.     "Ymodem", 11, (IS_NUMEROUS | RIGAMAROLE | IS_DL | NEEDS_FIN | NEEDS_HDR),
  112.     "Ymodem BATCH", "Ymodem SINGLE", "ymdown.blb", "ymodemup.blb",
  113.     sendYMChar, YM_BLOCK_SIZE, YMHdr, XYClear
  114.  
  115.     }
  116.   ,
  117.   #ifdef WXMODEM_AVAILABLE
  118.     {
  119.     "Wxmodem", 13, (RIGAMAROLE | IS_DL), "WXModem",
  120.     "WXModem", "wxdown.blb", "wxup.blb", sendWXModem, SECTSIZE,
  121.     NULL, ClearWX
  122.  
  123.     }
  124.   #else
  125.     {
  126.     NULL, 13, NOT_AVAILABLE, NULL, NULL, NULL, NULL, NULL, 0,
  127.     NULL, NULL
  128.  
  129.     }
  130.   #endif
  131.  
  132.   };
  133. int fixVers = 603;
  134. int majorVers = 114;
  135. char *netVersion = "1.16";
  136. extern CONFIG    cfg;   /* Lots an lots of variables    */
  137. extern logBuffer   logBuf;    /* Person buffer    */
  138. extern logBuffer   logTmp;    /* Person buffer    */
  139. extern aRoom     roomBuf;   /* Room buffer    */
  140. extern rTable    *roomTab;
  141. extern MessageBuffer     msgBuf;
  142. extern MessageBuffer     tempMess;
  143. extern NetBuffer   netBuf;
  144. extern int     outPut;
  145. extern char    onConsole;
  146. extern AN_UNSIGNED crtColumn; /* where are we on screen now?  */
  147. extern char    loggedIn;  /* Is we logged in?     */
  148. extern char    outFlag;    /* Output flag     */
  149. extern char    haveCarrier;    /* Do we still got carrier?     */
  150. extern char    heldMess;
  151. extern int     TransProtocol;  /* transfer protocol in use     */
  152. extern char    prevChar;  /* previous char output   */
  153. extern char    textDownload;   /* flag     */
  154. extern int     thisRoom;
  155. extern int     thisLog;
  156. extern char    whichIO;    /* Where I/O is     */
  157. extern char    echo;     /* Should we echo? echo? echo?  */
  158. extern FILE    *msgfl;
  159. extern FILE    *roomfl;
  160. extern FILE    *logfl;
  161. extern int     exitValue;
  162. extern char    *LCHeld, *WRITE_ANY, *WRITE_TEXT;
  163. extern char    PrintBanner;
  164. FunnyInfo Formats[] =
  165.   {
  166.     {   "LHA", TRUE,  LZHDir    },
  167.     {   "ZIP", TRUE,  ZIPDir    },
  168.     {   "ZOO", TRUE,  ZOODir    },
  169.     {   "ARC", TRUE,  ARCDir    },
  170.     {   "LZH", TRUE,  LZHDir    },
  171.     {   "GIF", FALSE, GifDir    },
  172.     {   "FRA", FALSE, GifDir    },
  173.     {    NULL, FALSE, NULL    },
  174.   };
  175. /*
  176. * CompressType()
  177. *
  178. * This function finds the type of file the specified file is.
  179. */
  180. int CompressType(char *name)
  181.   {
  182.   int format;
  183.   char *c;
  184.   if ((c = strchr(name, '.')) != NULL)
  185.     {
  186.     for (format = 0; Formats[format].Format != NULL; format++)
  187.       {
  188.       if (strCmpU(c + 1, Formats[format].Format) == SAMESTRING)return format;
  189.  
  190.       };
  191.  
  192.     }
  193.   return -1;
  194.  
  195.   }
  196. /*
  197. * AsciiHeader()
  198. *
  199. * This will entitle an ASCII file transfer.
  200. */
  201. int AsciiHeader(long fileSize, char *filename)
  202.   {
  203.   char work[10];
  204.   doCR();
  205.   mPrintf("[ %s : %s bytes ]", filename, PrintPretty(fileSize, work));
  206.   doCR();
  207.   doCR();
  208.   return TRUE;
  209.  
  210.   }
  211. #define FN_LENGTH 90
  212. /*
  213. * CompressedDir()
  214. *
  215. * This function reads the TOC of compressed files using a table of function
  216. * pointers (for generic use) and displays it.
  217. */
  218. void CompressedDir(DirEntry *fn)
  219.   {
  220.   FILE  *fd;
  221.   char  FileName[FN_LENGTH];
  222.   char  DateStr[20];
  223.   long  RealSize, SmallSize;
  224.   int   count = 0;
  225.   long  compressed = 0l, realsize = 0l;
  226.   int   format;
  227.   extern char *READ_ANY;
  228.   extern int DirAlign;
  229.   extern char AlignChar;
  230.   if (outFlag != OUTOK) return;
  231.   if ((format = CompressType(fn->unambig)) == ERROR) return;
  232.   mPrintf("\n %s", fn->unambig);
  233.   if (FindFileComment(fn->unambig))
  234.     {
  235.     DirAlign = strLen(fn->unambig) + 3;
  236.     AlignChar = 0;
  237.     mPrintf(":%s", strchr(msgBuf.mbtext, ' '));
  238.     DirAlign = 0;
  239.  
  240.     }
  241.   mPrintf("\n ");
  242.   if ((fd = fopen(fn->unambig, READ_ANY)) == NULL)
  243.     {
  244.     mPrintf("INTERNAL FILE ERROR!\n ");
  245.     return ;
  246.  
  247.     }
  248.   if (Formats[format].Many)
  249.     {
  250.     mPrintf("\n%7s   %8s%6s Name \n ", "Crunched", "Normal  ", " Date ");
  251.     while ((*Formats[format].Func)(fd, FileName, &RealSize, &SmallSize,
  252.     DateStr))
  253.       {
  254.       count++;
  255.       mPrintf("\n %7ld %8ld %6s %s", SmallSize, RealSize, DateStr,FileName);
  256.       compressed += SmallSize;
  257.       realsize   += RealSize;
  258.  
  259.       }
  260.     mPrintf("\n  ------- --------");
  261.     mPrintf("\n %7ld %8ld %d files\n ", compressed, realsize, count);
  262.  
  263.     }
  264.   else
  265.     {
  266.     (*Formats[format].Func)(fd, TRUE, msgBuf.mbtext);
  267.     mPrintf("%s", msgBuf.mbtext);
  268.  
  269.     }
  270.   fclose(fd);
  271.  
  272.   }
  273. /*
  274. * ARCDir()
  275. *
  276. * This function reads an ARC TOC entry and sets for the next one.
  277. */
  278. char ARCDir(FILE *fd, char *FileName, long *RSize, long *SSize, char *DateStr)
  279.   {
  280.   ARCbuf buf;
  281.   #ifndef IS_MOTOROLA
  282.   if (fread(&buf, sizeof buf, 1, fd) <= 0)
  283.   return FALSE;
  284.   #else
  285.   /* this mess is due to Lattice C doing structure padding on Amigas */
  286.   fread(&buf.ArchiveMark, 1, 1, fd);
  287.   fread(&buf.Header, 1, 1, fd);
  288.   fread(buf.name, 13, 1, fd);
  289.   fread(&buf.size, 4, 1, fd);
  290.   fread(&buf.date, 2, 1, fd);
  291.   fread(&buf.time, 2, 1, fd);
  292.   fread(&buf.crc, 2, 1, fd);
  293.   fread(&buf.length, 4, 1, fd);
  294.   #endif
  295.   if (buf.ArchiveMark != 0x1a || buf.Header == 0)
  296.   return FALSE;
  297.   strCpy(FileName, buf.name);
  298.   #ifdef IS_MOTOROLA
  299.   Intel32ToMotorola(&buf.size);
  300.   Intel32ToMotorola(&buf.length);
  301.   Intel16ToMotorola(&buf.date);
  302.   #endif
  303.   *SSize = buf.size;
  304.   *RSize = buf.length;
  305.   DosToNormal(DateStr, buf.date);
  306.   fseek(fd, buf.size, SEEK_CUR);
  307.   return TRUE;
  308.  
  309.   }
  310. /*
  311. * ZIPDir()
  312. *
  313. * This function reads a ZIP TOC entry and sets for the next one.
  314. */
  315. char ZIPDir(FILE *fd, char *FileName, long *RSize, long *SSize, char *DateStr)
  316.   {
  317.   ZipHeader ZBuf;
  318.   if (fread(&ZBuf, sizeof ZBuf, 1, fd) < 1) return FALSE;
  319.   #ifdef IS_MOTOROLA
  320.   Intel32ToMotorola(&ZBuf.Signature);
  321.   Intel32ToMotorola(&ZBuf.CompSize);
  322.   Intel32ToMotorola(&ZBuf.NormalSize);
  323.   Intel16ToMotorola(&ZBuf.NameLength);
  324.   Intel16ToMotorola(&ZBuf.FieldLength);
  325.   Intel16ToMotorola(&ZBuf.FileDate);
  326.   #endif
  327.   if (ZBuf.Signature != 0x04034b50) return FALSE;
  328.   if (ZBuf.NameLength < 0 || ZBuf.NameLength > FN_LENGTH) return FALSE;
  329.   fread(FileName, ZBuf.NameLength, 1, fd);
  330.   FileName[ZBuf.NameLength] = 0;
  331.   fseek(fd, ZBuf.FieldLength + ZBuf.CompSize, 1);
  332.   *SSize = ZBuf.CompSize;
  333.   *RSize = ZBuf.NormalSize;
  334.   DosToNormal(DateStr, ZBuf.FileDate);
  335.   return TRUE;
  336.   }
  337.   /*
  338.   * GifDir()
  339.   *
  340.   * This reads the important data of a GIF file.
  341.   */
  342.   char GifDir(FILE *fd, char longexpl, char *sbuf)
  343.     {
  344.     GifHeader buf;
  345.     fread(&buf, sizeof buf, 1, fd);
  346.     #ifdef IS_MOTOROLA
  347.     Intel16ToMotorola(&buf.Width);
  348.     Intel16ToMotorola(&buf.Height);
  349.     #endif
  350.     sbuf[0] = 0;
  351.     if (longexpl)
  352.     sPrintf(sbuf, "File is %.6s, ", buf.Sig);
  353.     sPrintf(lbyte(sbuf), (longexpl) ? "%d X %d, %d colors.\n " :
  354.     "%3d X %3d X %2d", buf.Width, buf.Height,
  355.     1 << ((buf.Colors & 0x07) + 1));
  356.     return TRUE;
  357.  
  358.     }
  359.   /*
  360.   * ZOODir()
  361.   *
  362.   * This handles reading a Zoo entry.
  363.   */
  364.   char ZOODir(FILE *fd, char *FileName, long *RSize, long *SSize, char *DateStr)
  365.     {
  366.     zoo_header zh;
  367.     zoo_direntry de;
  368.     static char ZooStart = TRUE;
  369.     if (ZooStart)
  370.       {
  371.       ZooStart = FALSE;
  372.       fread(&zh, sizeof zh, 1, fd);
  373.       #ifdef IS_MOTOROLA
  374.       Intel32ToMotorola(&zh.zoo_start);
  375.       #endif
  376.       fseek(fd, zh.zoo_start, 0);
  377.  
  378.       }
  379.     do
  380.       {
  381.       if (fread(&de, sizeof de, 1, fd) < 1)
  382.         {
  383.         ZooStart = TRUE;
  384.         return FALSE;
  385.  
  386.         }
  387.       #ifdef IS_MOTOROLA
  388.       Intel32ToMotorola(&de.next);
  389.       #endif
  390.       fseek(fd, de.next, 0);
  391.  
  392.       }
  393.     while (de.deleted);
  394.     #ifdef IS_MOTOROLA
  395.     Intel32ToMotorola(&de.size_now);
  396.     Intel32ToMotorola(&de.org_size);
  397.     Intel16ToMotorola(&de.date);
  398.     #endif
  399.     *SSize = de.size_now;
  400.     *RSize = de.org_size;
  401.     strcpy(FileName, de.fname);
  402.     DosToNormal(DateStr, de.date);
  403.     return TRUE;
  404.  
  405.     }
  406.   /*
  407.   * LZHDir()
  408.   *
  409.   * This function handles reading a lzh entry.
  410.   *
  411.   * courtesy Daniel Durbin.
  412.   */
  413.   char LZHDir(FILE *fd, char *FileName, long *RSize, long *SSize, char *DateStr)
  414.     {
  415.     LZHead header;
  416.     #ifndef IS_MOTOROLA
  417.     if (fread(&header, sizeof(header), 1, fd) < 1)
  418.     return FALSE;
  419.     #else
  420.     if (fread(header.unknown1, sizeof header.unknown1, 1, fd) < 1)
  421.     return FALSE;
  422.     if (fread(header.method, sizeof header.method, 1, fd) < 1)
  423.     return FALSE;
  424.     if (fread(&header.csize, sizeof header.csize, 1, fd) < 1)
  425.     return FALSE;
  426.     if (fread(&header.fsize, sizeof header.fsize, 1, fd) < 1)
  427.     return FALSE;
  428.     if (fread(&header.ftime, sizeof header.ftime, 1, fd) < 1)
  429.     return FALSE;
  430.     if (fread(&header.fdate, sizeof header.fdate, 1, fd) < 1)
  431.     return FALSE;
  432.     if (fread(&header.fattr, sizeof header.fattr, 1, fd) < 1)
  433.     return FALSE;
  434.     if (fread(&header.unknown2, sizeof header.unknown2, 1, fd) < 1)
  435.     return FALSE;
  436.     if (fread(&header.namelen, sizeof header.namelen, 1, fd) < 1)
  437.     return FALSE;
  438.     #endif
  439.     if (header.namelen < 1 || header.namelen >= FN_LENGTH)
  440.     return FALSE;
  441.     if (fread(FileName, 1, header.namelen, fd) != header.namelen)
  442.     return FALSE;
  443.     FileName[header.namelen] = 0;
  444.     fgetc(fd), fgetc(fd); /* gets CRC I guess */
  445.     /* I don't* know what this is jumping over - but it works! */
  446.     if (header.unknown2 == 1)
  447.       {
  448.       fgetc(fd);
  449.       fgetc(fd);
  450.       fgetc(fd);
  451.  
  452.       }
  453.     #ifdef IS_MOTOROLA
  454.     Intel32ToMotorola(&header.fsize);
  455.     Intel32ToMotorola(&header.csize);
  456.     Intel16ToMotorola(&header.fdate);
  457.     #endif
  458.     *SSize = header.csize;
  459.     *RSize = header.fsize;
  460.     DosToNormal(DateStr, header.fdate);
  461.     fseek(fd, header.csize, SEEK_CUR);
  462.     return TRUE;
  463.  
  464.     }
  465.   /*
  466.   * DosToNormal()
  467.   *
  468.   * This function converts a DOS-formatted date to a formatted string.  This
  469.   * perhaps should reside in the system dependent code....
  470.   */
  471.   void DosToNormal(char *DateStr, UNS_16 DosDate)
  472.     {
  473.     if (((DosDate & 0x1e0) >> 5) > 12 ||
  474.     ((DosDate & 0x1e0) >> 5) < 1)
  475.     strCpy(DateStr, "No Date");
  476.     else
  477.     sprintf(DateStr, "%d%s%02d", ((DosDate & 0xfe00) >> 9) + 80,
  478.     monthTab[(DosDate & 0x1e0) >> 5], DosDate & 0x1f);
  479.  
  480.     }
  481.   /*
  482.   * calcrc()
  483.   *
  484.   * Calculates CRC for a given block.
  485.   */
  486.   CRC_TYPE calcrc(unsigned char *ptr, int count)
  487.     {
  488.     register CRC_TYPE checksum;
  489.     register int i;
  490.     checksum=0;
  491.     while (count--)
  492.       {
  493.       i=(checksum >> 8) & 0xff;
  494.       i ^= *ptr++;
  495.       i ^= i >> 4;
  496.       checksum <<= 8;
  497.       checksum ^= i;
  498.       i <<= 5;
  499.       checksum ^= i;
  500.       i <<= 7;
  501.       checksum ^= i;
  502.  
  503.       }
  504.     return(checksum);
  505.  
  506.     }
  507.   #ifdef NEED_AVAILABLE
  508.   /************************************************************************/
  509.   /*  changedate() gets the date from the aide and remembers it */
  510.   /************************************************************************/
  511.   void changeDate()
  512.     {
  513.     int year, day, hours, minutes, mon;
  514.     char *month;
  515.     Output_Citadel_Message("CHNGDT",NULL,NULL,NULL);
  516.     if (!getYesNo("ETDTMN"))
  517.     return ;
  518.     do
  519.       {
  520.       year    = (int) getNumber("YEARPR",  87l, 99l) + 1900;
  521.       mon     = (int) getNumber("MONTPR", 1l,  12l)     ;
  522.       day     = (int) getNumber("DAYPRM",   1l,  31l)     ;
  523.       hours   = (int) getNumber("HOURPR",   0l, 23l)     ;
  524.       minutes = (int) getNumber("MINTPR", 0l, 59l)     ;
  525.  
  526.       }
  527.     while (!setRawDate(year, mon, day, hours, minutes));
  528.     /*    InitEvents(); */
  529.  
  530.     }
  531.   #endif
  532.   /*
  533.   * CheckDLimit()
  534.   *
  535.   * This checks to see if the next d/l will exceed the the limit or if it'll
  536.   * interfere with a preemptive event.  It returns FALSE on interference,
  537.   * TRUE otherwise.
  538.   */
  539.   char CheckDLimit(long estimated)
  540.     {
  541.     char *problem;
  542.     extern char *DlMsgPtr;
  543.     if (!aide && Dl_Limit_On() &&
  544.     (*DL_Total) + estimated >= Dl_Limit * 60)
  545.       {
  546.       mPrintf("I'm sorry, that would exceed the current cumulative download time limit ");
  547.       if (strLen(DlMsgPtr) != 0)
  548.       mPrintf("of %s", DlMsgPtr);
  549.       mPrintf(" -- you've currently spent %ld:%02ld in downloading.\n ",
  550.       (*DL_Total) / 60l, (*DL_Total) % 60l);
  551.       return FALSE;
  552.  
  553.       }
  554.     if ((problem = ChkPreempt(estimated)) != NULL)
  555.       {
  556.       mPrintf("Sorry, that would interfere with %s.\n ", problem);
  557.       return FALSE;
  558.  
  559.       }
  560.     return TRUE;
  561.  
  562.     }
  563.   /*
  564.   * civTime()
  565.   *
  566.   * Military time to Civilian time.
  567.   */
  568.   void civTime(int *hours, char **which)
  569.     {
  570.     if (*hours >= 12)
  571.     *which = "pm";
  572.     else
  573.     *which = "am";
  574.     if (*hours >= 13)
  575.     *hours -= 12;
  576.     if (*hours == 0)
  577.     *hours = 12;
  578.  
  579.     }
  580.   /*
  581.   * configure()
  582.   *
  583.   * This sets up the terminal width etc via dialogue.
  584.   */
  585.   char configure(logBuffer *lBuf, char AllQuestions, char AllowAbort)
  586.     {
  587.     int width, xwidth;    /* really! ugly kludge -- fix someday */
  588.     lBuf->lbnulls   = 0;
  589.     logBuf.lbdelay  = 0;
  590.     width = termWidth;
  591.     do
  592.       {
  593.       /* this gross width stuff is caused by that #define in ctdl.h */
  594.       termWidth = width;
  595.       lBuf->lbwidth   = (int) getNumber("COLSCR", 0l, 132l);
  596.       if( !gotCarrier() )
  597.         {
  598.         haveCarrier      = FALSE;
  599.         };
  600.       xwidth = lBuf->lbwidth;
  601.       if (onLine() && lBuf->lbwidth == 0 && AllowAbort)
  602.         {
  603.         termWidth = width;
  604.         if (getYesNo("ABORTA")) return FALSE;
  605.  
  606.         }
  607.       if (lBuf->lbwidth < 40)
  608.         {
  609.         termWidth = width;
  610.         Output_Citadel_Message("WIDT40",NULL, NULL, NULL);
  611.         }
  612.       lBuf->lbwidth = xwidth;
  613.  
  614.       }
  615.     while ( onLine() && lBuf->lbwidth < 40 );
  616.     if (AllQuestions)
  617.       {
  618.       lBuf->lbflags.LFMASK = getYesNo("NEEDLF") ? TRUE : FALSE;
  619.  
  620.       }
  621.     else
  622.       {
  623.       Output_Citadel_Message("XXXXXX",NULL,NULL,NULL);
  624.       lBuf->lbflags.LFMASK = getYesNo("ISBLNK") ? FALSE : TRUE;
  625.  
  626.       }
  627.     lBuf->lbflags.EXPERT      = getYesNo("AREEXP") ? TRUE : FALSE;
  628.     if (lBuf->lbflags.EXPERT || AllQuestions)
  629.       {
  630.       lBuf->lbflags.TIME      = getYesNo("PRTTME") ? TRUE : FALSE;
  631.       lBuf->lbflags.OLDTOO    = getYesNo("PRTOLD") ? TRUE : FALSE;
  632.       lBuf->lbflags.FLOORS    = getYesNo("FLRMOD");
  633.       lBuf->lbflags.ANSI      = getYesNo("ANSIHD");
  634.       lBuf->lbflags.MSG_PAUSE = getYesNo("MSGPAS");
  635.       }
  636.     else
  637.       {
  638.       lBuf->lbflags.OLDTOO    = FALSE;
  639.       lBuf->lbflags.TIME      = TRUE;
  640.       lBuf->lbflags.FLOORS    = TRUE;
  641.       lBuf->lbflags.HALF_DUP  = FALSE;
  642.       lBuf->lbflags.ANSI      = FALSE;  /* No ANSI colors */
  643.       lBuf->lbflags.MSG_PAUSE = FALSE;  /* no pause       */
  644.       }
  645.     return TRUE;
  646.  
  647.     }
  648.   /*
  649.   * crashout()
  650.   *
  651.   * Problems?  Out we go!!! This is a general error exit function.
  652.   */
  653.   void crashout(char *message)
  654.     {
  655.     FILE *fd;       /* Record some crash data */
  656.     exitValue = CRASH_EXIT;
  657.     outFlag = IMPERVIOUS;
  658.     Output_Citadel_Message("REDALT",(long)message,NULL,NULL);
  659.     HangUp(FALSE);
  660.     logMessage(L_OUT, "", 0);
  661.     logMessage(CRASH_OUT, "", 0);
  662.     if( (fd = safeopen("crash", "w")) != NULL )
  663.       {
  664.       fprintf(fd, message);
  665.       fclose(fd);
  666.       };
  667.     writeSysTab();
  668.     ModemShutdown(TRUE);
  669.     systemShutdown(0);
  670.     exit(exitValue);
  671.  
  672.     }
  673.   /*
  674.   * doFormatted()
  675.   *
  676.   * This does a tutorial for a wildCard call.
  677.   */
  678.   void doFormatted(DirEntry *fn)
  679.     {
  680.     tutorial(fn->unambig, FALSE);
  681.  
  682.     }
  683.   /*
  684.   * doCR()
  685.   *
  686.   * This does a newline on modem and console.
  687.   */
  688.   void doCR()
  689.     {
  690.     int i;
  691.     crtColumn   = 1;
  692.     if (outFlag != OUTOK &&     /* output is being s(kip)ped    */
  693.     outFlag != IMPERVIOUS)
  694.     return;
  695.     if (outPut == DISK) fprintf(upfd, "\n");
  696.     else
  697.       {
  698.       if (TransProtocol == ASCII)
  699.       mputChar(NEWLINE);
  700.       if (haveCarrier)
  701.         {
  702.         (*Table[TransProtocol].method)('\r');
  703.         if (TransProtocol == ASCII)
  704.         for (i = termNulls;  i;  i--) outMod(0);
  705.         if (termLF)
  706.         (*Table[TransProtocol].method)('\n');
  707.  
  708.         }
  709.       /* Kludge alert!  Kludge alert! */
  710.       /* We don't have to check TransProtocol, though. */
  711.       if (DirAlign != 0 && termWidth > 22)
  712.         {
  713.         #ifndef TURBO_C_VSPRINTF_BUG
  714.         mPrintf("%*c%c ", DirAlign, ' ', AlignChar);
  715.         #else
  716.         /* SUPER YUCKY! */
  717.         crtColumn += DirAlign + 1;
  718.         for (i = 0; i < DirAlign; i++)
  719.           {
  720.           mputChar(' ');
  721.           if (haveCarrier)
  722.           (*Table[TransProtocol].method)(' ');
  723.  
  724.           }
  725.         mputChar(AlignChar);
  726.         mputChar(' ');
  727.         if (haveCarrier)
  728.           {
  729.           (*Table[TransProtocol].method)(AlignChar);
  730.           (*Table[TransProtocol].method)(' ');
  731.  
  732.           }
  733.         #endif
  734.  
  735.         }
  736.  
  737.       }
  738.     prevChar    = ' ';
  739.  
  740.     }
  741.   /*
  742.   * download()
  743.   *
  744.   * This is the is the menu-level send-message-via-protocol function.
  745.   */
  746.   void download(char whichMess, char revOrder, char protocol, char global,
  747.   int Compression)
  748.     {
  749.     char result;
  750.     int  count;
  751.     extern char *APPEND_TEXT, Showing;
  752.     char CompFile[30];
  753.     outFlag     = OUTOK;
  754.     if (Table[protocol].MsgTran != NULL)
  755.        Output_Citadel_Message("DLFUNC",
  756.                 (long)(InternalProtocol(protocol) ? Table[protocol].MsgTran :
  757.                 FindProtoName(protocol)), NULL, NULL);
  758.     if (Compression != NO_COMP)
  759.        Output_Citadel_Message("DLCMPR",(long)GetCompEnglish(Compression), NULL, NULL);
  760.     if (InternalProtocol(protocol) && !expert &&
  761.     Table[protocol].BlbName != NULL)
  762.     tutorial(Table[protocol].BlbName, TRUE);
  763.     if (protocol != ASCII)
  764.       {
  765.       if (!getYesNo("RDYBEG"))  return;
  766.  
  767.       }
  768.     if (!InternalProtocol(protocol) || Compression != NO_COMP)
  769.       {
  770.       result = TRAN_SUCCESS;
  771.       ToTempArea();
  772.       Output_Citadel_Message("DELAYM",NULL,NULL,NULL);
  773.       echo = NEITHER;
  774.       if (!redirect("msgs")) return;
  775.  
  776.       }
  777.     else
  778.       {
  779.       if (protocol != ASCII) echo = NEITHER;
  780.       result = Transmission(protocol, STARTUP);
  781.  
  782.       }
  783.     if (protocol != ASCII || Compression != NO_COMP) Showing = DL_MSGS;
  784.     if (result == TRAN_SUCCESS)
  785.       {
  786.       if (!global)
  787.         {
  788.         count = showMessages(whichMess, revOrder,
  789.         logBuf.lbvisit[logBuf.lbgen[thisRoom] & CALLMASK],
  790.         OptionValidate);
  791.         if (count == 0) Output_Citadel_Message("NONEWM",NULL,NULL,NULL);
  792.  
  793.         }
  794.       else
  795.       doGlobal(whichMess, revOrder);
  796.       if (!InternalProtocol(protocol) || Compression != NO_COMP)
  797.         {
  798.         undirect();
  799.         if (Compression != NO_COMP)
  800.           {
  801.           sPrintf(CompFile, "msgs.%s", CompExtension(Compression));
  802.           Compress(Compression, "msgs", CompFile);
  803.           unlink("msgs"); /* because we use TranSend to send files */
  804.           if (access(CompFile, 0) != 0)
  805.             {
  806.             mPrintf("Error: The compression failed.\n ");
  807.             KillTempArea();
  808.             return;
  809.  
  810.             }
  811.  
  812.           }
  813.         else strCpy(CompFile, "msgs");
  814.         TranSend(protocol, transmitFile, ALL_FILES, "", FALSE);
  815.         unlink("msgs");
  816.         unlink(CompFile);
  817.         KillTempArea();
  818.  
  819.         }
  820.       else Transmission(TransProtocol, FINISH);
  821.       Showing = WHATEVER;
  822.  
  823.       }
  824.     echo = BOTH;
  825.     TransProtocol = ASCII;
  826.     /*
  827.     * If we have a console timeout during message display (during a Pause,
  828.     * most likely), onLine() will not be true at this point.  But setUp()
  829.     * will blindly set it to TRUE, so we have to call this with some care.
  830.     */
  831.     if (onLine())
  832.     setUp(FALSE);
  833.  
  834.     }
  835.   int StartingRoom, CurRoom;
  836.   /*
  837.   * doGlobal()
  838.   *
  839.   * Does .R{Y,W,X,other protocols}G
  840.   */
  841.   void doGlobal(char whichMess, char revOrder)
  842.     {
  843.     extern char PhraseUser;
  844.     StartingRoom = CurRoom = thisRoom;
  845.     while (
  846.     ((whichMess == NEWoNLY && !PhraseUser) ? gotoRoom("", 'R') : NextSeq())
  847.     && (gotCarrier() || onConsole))
  848.       {
  849.       givePrompt();
  850.       mPrintf("read\n ");
  851.       showMessages(whichMess, revOrder,
  852.       logBuf.lbvisit[logBuf.lbgen[thisRoom] & CALLMASK],
  853.       OptionValidate);
  854.       doCR();   /* aesthetics, pig-dogs. */
  855.       if (outFlag == OUTSKIP) break;
  856.  
  857.       }
  858.  
  859.     }
  860.   /*
  861.   * NextSeq()
  862.   *
  863.   * This finds next room in sequence for doGlobal().
  864.   */
  865.   int NextSeq()
  866.     {
  867.     int i;
  868.     i = (CurRoom + 1) % MAXROOMS;
  869.     while (i != StartingRoom)
  870.       {
  871.       if (roomTab[i].rtflags.INUSE &&
  872.       KnownRoom(i) != UNKNOWN_ROOM)
  873.         {
  874.         getRoom(i);
  875.         CurRoom = i;
  876.         return TRUE;
  877.  
  878.         }
  879.       i = (i + 1) % MAXROOMS;
  880.  
  881.       }
  882.     return FALSE;
  883.  
  884.     }
  885.   /*
  886.   * formHeader()
  887.   *
  888.   * This returns a string with the msg header formatted.
  889.   */
  890. char *formHeader()
  891.     {
  892.     static char header[250];
  893.     header[0] = 0;      /* Initialize the genie.... */
  894.     if (msgBuf.mbdate[ 0])  sPrintf(lbyte(header), "  %s ", msgBuf.mbdate);
  895.     if (msgBuf.mbtime[ 0] && sendTime) sPrintf(lbyte(header), "%s ", msgBuf.mbtime);
  896.     if (msgBuf.mbauth[ 0]) sPrintf(lbyte(header), "from %s" ,msgBuf.mbauth );
  897.     NormStr(msgBuf.mboname);
  898.     if (msgBuf.mboname[0])
  899.       {
  900.       sPrintf(lbyte(header), " @ %s", msgBuf.mboname);
  901.       if (msgBuf.mbdomain[0])sPrintf(lbyte(header), cfg.DomainDisplay, msgBuf.mbdomain);
  902.       };
  903.     if (strCmpU(msgBuf.mbroom, roomBuf.rbname) != SAMESTRING)
  904.       {
  905.       strCat(header, " in ");
  906.       if (roomExists(msgBuf.mbroom) != ERROR)
  907.       sPrintf(lbyte(header), formRoom(roomExists(msgBuf.mbroom), FALSE,
  908.       FALSE));
  909.       else
  910.       sPrintf(lbyte(header), "%s>", msgBuf.mbroom);
  911.  
  912.       };
  913.     if (msgBuf.mbto[   0])
  914.       {
  915.       sPrintf(lbyte(header), " to %s" , msgBuf.mbto);
  916.       if (!msgBuf.mbauth[0] && thisRoom == MAILROOM &&
  917.       strLen(cfg.SysopName) != 0)     /* Mail to sysop */
  918.       sPrintf(lbyte(header), " (%s)", cfg.SysopName);
  919.  
  920.       };
  921.     if (msgBuf.mbaddr[ 0] &&
  922.     strncmp(msgBuf.mbaddr, R_SH_MARK, strLen(R_SH_MARK)) != SAMESTRING &&
  923.     strncmp(msgBuf.mbaddr, LOC_NET, strLen(LOC_NET)) != SAMESTRING &&
  924.     strncmp(msgBuf.mbaddr, NON_LOC_NET, strLen(NON_LOC_NET)) != SAMESTRING)
  925.     sPrintf(lbyte(header), " (on %s)", strCmpU(msgBuf.mbaddr, ALL_LOCALS) ?
  926.     msgBuf.mbaddr : "All Local Systems");
  927.     return header;
  928.  
  929.     }
  930.   /*
  931.   * formRoom()
  932.   *
  933.   * This returns a string with the room formatted, including the prompt type.
  934.   */
  935.   char display[40];
  936.   char matrix[2][2] =
  937.     {
  938.       { '>', ')'  },
  939.       { ']', ':'  }
  940.  
  941.     };
  942.  
  943. #define ANSII_NONE   ""       /* reset, ansii off */
  944. #define ANSII_RED    ""   /* RED    */
  945. #define ANSII_GREEN  ""   /* GREEN  */
  946. #define ANSII_ORANGE ""   /* ORANGE */
  947. #define ANSII_BLUE   ""   /* BLUE   */
  948.  
  949.  
  950. char *ccode[4] =
  951.     {
  952.     ANSII_ORANGE,  /* Orange: Regular, no directory   */
  953.     ANSII_RED,     /* RED:    Networked, no directory */
  954.     ANSII_GREEN,   /* GREEN:  Regular, Directory      */
  955.     ANSII_BLUE     /* BLUE:   Networked, Directory    */
  956.     };
  957.  
  958. char *formRoom(int roomNo, int showPriv, int noDiscrimination)
  959.     {
  960.     int   one, two, color;
  961.     one = roomTab[roomNo].rtflags.ISDIR;
  962.     two = (roomTab[roomNo].rtflags.SHARED && cfg.BoolFlags.netParticipant);
  963.     if (roomTab[roomNo].rtflags.INUSE)
  964.       {
  965.       if (!noDiscrimination && !roomTab[roomNo].rtflags.PUBLIC)
  966.         strcpy(display, APrivateRoom);
  967.       else
  968.         {
  969.         if( logBuf.lbflags.ANSI )
  970.           {
  971.           color = one*2 + two;
  972.           sPrintf(display, "%s%s%s%c%s"
  973.           ,ccode[color], roomTab[roomNo].rtname,ANSII_NONE, matrix[one][two],
  974.           (!roomTab[roomNo].rtflags.PUBLIC && showPriv) ? "*" : "");
  975.           }
  976.         else
  977.           {
  978.           sPrintf(display, "%s%c%s", roomTab[roomNo].rtname, matrix[one][two],
  979.           (!roomTab[roomNo].rtflags.PUBLIC && showPriv) ? "*" : "");
  980.           };
  981.         };
  982.       }
  983.     else display[0] = '\0';
  984.     return display;
  985.  
  986.     }
  987.   /*
  988.   * getCdate()
  989.   *
  990.   * This retrieves system date and returns in the parameters.
  991.   */
  992.   void getCdate(int *year, char **month, int *day, int *hours, int *minutes)
  993.     {
  994.     int mon, seconds, milli;
  995.     getRawDate(year, &mon, day, hours, minutes, &seconds, &milli);
  996.     *year -= 1900;
  997.     *month = monthTab[mon];
  998.  
  999.     }
  1000.   /*
  1001.   * GetSecond()
  1002.   *
  1003.   * This will return the second of the minute.  For multibanner.
  1004.   */
  1005.   int GetSecond()
  1006.     {
  1007.     int y, d, h, m, seconds, ml, mon;
  1008.     getRawDate(&y, &mon, &d, &h, &m, &seconds, &ml);
  1009.     return seconds;
  1010.  
  1011.     }
  1012.   /*
  1013.   * HelpIfPresent()
  1014.   *
  1015.   * This will print help file if present, but not complain.
  1016.   */
  1017.   char HelpIfPresent(char *filename)
  1018.     {
  1019.     SYS_FILE fn;
  1020.     makeSysName(fn, filename, &cfg.homeArea);
  1021.     if (access(fn, 4) == 0)
  1022.       {
  1023.       tutorial(filename, TRUE);
  1024.       return TRUE;
  1025.  
  1026.       }
  1027.     else return FALSE;
  1028.  
  1029.     }
  1030.   /*
  1031.   * ingestFile()
  1032.   *
  1033.   * This puts the given file in the held msg buffer.
  1034.   */
  1035.   char ingestFile(char *name, MessageBuffer *msg)
  1036.     {
  1037.     char  filename[100];  /* Paths, etc.... */
  1038.     FILE  *fd;
  1039.     int   c, d, index;
  1040.     extern char *READ_TEXT;
  1041.     strCpy(filename, name);
  1042.     if ((fd = safeopen(filename, READ_TEXT)) == NULL)
  1043.       {
  1044.       return FALSE;
  1045.  
  1046.       }
  1047.     index = (heldMess) ? strLen(msg->mbtext) : 0;
  1048.     while ((c = fgetc(fd)) != EOF && index < MAXTEXT - 2)
  1049.       {
  1050.       if (c)
  1051.         {
  1052.         if (c == '\n')
  1053.           {
  1054.           /*
  1055.           * this should shave off trailing spaces.
  1056.           */
  1057.           while (index - 1 >= 0 && msg->mbtext[index - 1] == ' ')
  1058.           index--;
  1059.           while (!(d = fgetc(fd)))   /* skip any following zero bytes */
  1060.           ;
  1061.           if (d == '\n' || d == ' ' || d == EOF)
  1062.             {
  1063.             msg->mbtext[index++] = c;
  1064.             if (d != EOF)
  1065.             msg->mbtext[index++] = d;
  1066.  
  1067.             }
  1068.           else if (d)
  1069.             {
  1070.             msg->mbtext[index++] = ' ';
  1071.             msg->mbtext[index++] = d;
  1072.  
  1073.             }
  1074.  
  1075.           }
  1076.         else msg->mbtext[index++] = c;
  1077.  
  1078.         }
  1079.  
  1080.       }
  1081.     msg->mbtext[index] = 0;
  1082.     fclose(fd);
  1083.     CleanEnd(msg->mbtext);
  1084.     if (msg == &tempMess) heldMess = TRUE;
  1085.     return TRUE;
  1086.  
  1087.     }
  1088.   /*
  1089.   * formDate()
  1090.   *
  1091.   * This function forms the current date.
  1092.   */
  1093.   char *formDate()
  1094.     {
  1095.     static char dateLine[40];
  1096.     int  day, year, h, m;
  1097.     char *month;
  1098.     getCdate(&year, &month, &day, &h, &m);
  1099.     sPrintf(dateLine, "%d%s%02d", year, month, day);
  1100.     return dateLine;
  1101.  
  1102.     }
  1103.   /*
  1104.   * Current_Time()
  1105.   *
  1106.   * This function will get the current time, format cutely.
  1107.   */
  1108.   char *Current_Time()
  1109.     {
  1110.     char  *ml, *month;
  1111.     int   year, day, h, m;
  1112.     static char Time[13];
  1113.     getCdate(&year, &month, &day, &h, &m);
  1114.     civTime(&h, &ml);
  1115.     sPrintf(Time, "%d:%02d %s", h, m, ml);
  1116.     return Time;
  1117.  
  1118.     }
  1119.   /*
  1120.   * MultiBanner()
  1121.   *
  1122.   * This function handles the multibanner feature.
  1123.   */
  1124.   char MultiBanner(char *str)
  1125.     {
  1126.     SYS_FILE temp;
  1127.     makeBanner(temp, str, GetSecond());
  1128.     if (access(temp, 4) == 0)
  1129.       {
  1130.       tutorial(temp, FALSE);
  1131.       return TRUE;
  1132.  
  1133.       }
  1134.     else return FALSE;
  1135.  
  1136.     }
  1137.   /*
  1138.   * putBufChar()
  1139.   *
  1140.   * This is used to upload messages via protocol.
  1141.   * returns: ERROR on problems else TRUE.
  1142.   */
  1143.   int putBufChar(int c)
  1144.     {
  1145.     char result;
  1146.     if (masterCount == MAXTEXT + 10) return TRUE;
  1147.     if (masterCount > MAXTEXT - 2) return ERROR;
  1148.     /* This is necessary for a ProComm bug */
  1149.     if (c == CPMEOF)
  1150.       {
  1151.       masterCount = MAXTEXT + 10;
  1152.       return TRUE;
  1153.  
  1154.       }
  1155.     c &= 0x7F;          /* strip high bit */
  1156.     result = cfg.filter[c];
  1157.     if (result == '\0')
  1158.       {
  1159.       return TRUE;
  1160.  
  1161.       }
  1162.     msgBuf.mbtext[masterCount++] = result;
  1163.     msgBuf.mbtext[masterCount]   = 0;   /* EOL just for luck    */
  1164.     return TRUE;
  1165.  
  1166.     }
  1167.   /*
  1168.   * putFLChar()
  1169.   *
  1170.   * This is used to upload files.
  1171.   * returns: ERROR on problems else TRUE.
  1172.   */
  1173.   int putFLChar(int c)
  1174.     {
  1175.     extern FILE *netLog;
  1176.     if (fputc(c, upfd) != EOF)  return TRUE;
  1177.     /* else */      splitF(netLog, "Write error: %d\n", ferror(upfd));
  1178.     return ERROR;
  1179.  
  1180.     }
  1181.   /*
  1182.   * reconfigure()
  1183.   *
  1184.   * This function reconfigures a user, depending on their selection on the
  1185.   * dot command.
  1186.   *
  1187.   * Note: returns TRUE on backspace, FALSE otherwise
  1188.   */
  1189.   char reconfigure()
  1190.     {
  1191.     extern char MeetDisabled;
  1192.     char  *ON  = "ON", *OFF = "OFF";
  1193.     label alias, domain;
  1194.     char  system[(2 * NAMESIZE) + 10];
  1195.     int cost;
  1196.     extern int thisNet;
  1197.     char *ConfgOpts[] =
  1198.       {
  1199.       "C(omplete Reconfigure)  ", "E(xpert)          ", "F(loor mode)\n",
  1200.       "H(alf-duplex mode)      ", "L(inefeeds)       ", "N(ulls)\n",
  1201.       "O(ld messsage on new)   ", "T(ime of messages)", "D(elay)\n",
  1202.       "W(idth in columns)      ", "\r",                 "\n",
  1203.       "Z(Old .RE)              ", "G(raphics)        ", "\b",
  1204.       "Mail Forwarding         ", "Y(ield after msg)   ",
  1205.       "Prompt (message entry)\n",
  1206.       " ", " ", ""
  1207.  
  1208.       };
  1209.     static char *NOW = "Now %s.";
  1210.     RegisterThisMenu("confg.mnu", ConfgOpts);
  1211.     if (cfg.BoolFlags.netParticipant)
  1212.     ExtraOption(ConfgOpts, "Address\n");
  1213.     if (!MeetDisabled && loggedIn)
  1214.       {
  1215.       ExtraOption(ConfgOpts, "Biography");
  1216.  
  1217.       }
  1218.     switch (GetMenuChar())
  1219.       {
  1220.       case '\b': mPrintf(" \b"); PushBack('\b'); return TRUE;
  1221.       case 'A':     /* Forwarding address on the network */
  1222.       if (!ReqNodeName("FWRDML", alias, domain, FALSE,
  1223.       FALSE, TRUE, FALSE, FALSE, &netBuf) &&
  1224.       onLine())
  1225.         {
  1226.         /* in case carrier is lost */
  1227.         if (SearchList(&MailForward, logBuf.lbname) == NULL)
  1228.         break;
  1229.         else if (getYesNo("STOPFM"))
  1230.           {
  1231.           KillData(&MailForward, logBuf.lbname);
  1232.           UpdateForwarding();
  1233.           break;
  1234.  
  1235.           }
  1236.  
  1237.         }
  1238.       else if (onLine())
  1239.         {
  1240.         /* in case carrier is lost */
  1241.         sPrintf(system, (strLen(domain) != 0) ? "%s _ %s" : "%s%s",
  1242.         alias, domain);
  1243.         if (strLen(domain) != 0) cost = FindCost(domain);
  1244.         else cost = !netBuf.nbflags.local;
  1245.         if (cost > logBuf.credit)
  1246.           Output_Citadel_Message("WARNCR",(long)system,NULL,NULL);
  1247.         getString("ALIASF", alias, NAMESIZE, 0);
  1248.         if (onLine())
  1249.           {
  1250.           /* i.e., didn't drop carrier */
  1251.           AddMailForward(logBuf.lbname, system,
  1252.           (strLen(alias) != 0) ? alias : logBuf.lbname);
  1253.           if (!logBuf.lbflags.NET_PRIVS)
  1254.             Output_Citadel_Message("NETPRI",NULL, NULL, NULL);
  1255.  
  1256.           }
  1257.  
  1258.         }
  1259.       break;
  1260.       case 'M':
  1261.       getString("ALIASM", alias, NAMESIZE, 0);
  1262.       if (strLen(alias) != 0)
  1263.         {
  1264.         if (findPerson(alias, &logTmp) == ERROR)
  1265.             Output_Citadel_Message("NOPERS",(long)alias, NULL, NULL);
  1266.         else if (strCmpU(logTmp.lbname, logBuf.lbname) == SAMESTRING)
  1267.             Output_Citadel_Message("YRNAME",NULL, NULL, NULL);
  1268.         else
  1269.         AddMailForward(logBuf.lbname, NULL, alias);
  1270.  
  1271.         }
  1272.       else if (getYesNo("STOPFM"))
  1273.         {
  1274.         KillLocalFwd(logBuf.lbname);
  1275.  
  1276.         }
  1277.       break;
  1278.       case 'B':
  1279.       EditBio();
  1280.       break;
  1281.       case 'C':
  1282.       configure(&logBuf, FALSE, FALSE);
  1283.       break;
  1284.       case 'D':
  1285.       logBuf.lbdelay   = (int) getNumber("MDELAY", 0l, 255l);
  1286.       break;
  1287.       case 'G':
  1288.       logBuf.lbflags.ANSI = getYesNo("ANSIHD");
  1289.       break;
  1290.       case 'Y':
  1291.       logBuf.lbflags.MSG_PAUSE = getYesNo("MSGPAS");
  1292.       break;
  1293.       case 'E':
  1294.       mPrintf(NOW, (expert = !expert) ? ON : OFF);
  1295.       break;
  1296.       case 'P':
  1297.       mPrintf(NOW,(logBuf.lbflags.NoPrompt=!logBuf.lbflags.NoPrompt) ? OFF : ON);
  1298.       break;
  1299.       case 'Z':
  1300.       mPrintf(NOW, (logBuf.lbflags.ALT_RE = !logBuf.lbflags.ALT_RE) ? ON : OFF);
  1301.       break;
  1302.       case 'F':
  1303.       mPrintf(NOW, (FloorMode = !FloorMode) ? ON : OFF);
  1304.       break;
  1305.       case 'H':
  1306.       mPrintf(NOW, (HalfDup = !HalfDup) ? ON : OFF);
  1307.       break;
  1308.       case 'L':
  1309.       mPrintf(NOW, (termLF = !termLF) ? ON : OFF);
  1310.       break;
  1311.       case 'N':
  1312.       termNulls   = (int) getNumber("NULLSN", 0l, 255l);
  1313.       break;
  1314.       case 'O':
  1315.       mPrintf(NOW, (oldToo = !oldToo) ? ON : OFF);
  1316.       break;
  1317.       case 'T':
  1318.       mPrintf(NOW, (sendTime = !sendTime) ? ON : OFF);
  1319.       break;
  1320.       case 'W':
  1321.       termWidth   = (int) getNumber("COLSCRNO", 40l, 132l);
  1322.       break;
  1323.       case '\r':
  1324.       case '\n':
  1325.         tutorial("confg.mnu", TRUE);
  1326.       case '?':
  1327.         Display_User_Configuration();
  1328.       break;
  1329.  
  1330.       }
  1331.     storeLog();         /* save the current log entry!*/
  1332.     return FALSE;
  1333.  
  1334.     }
  1335.  
  1336. static void Display_User_Configuration()
  1337.   {
  1338.   ForwardMail *address;
  1339.   mPrintf("\n Current user setup:");
  1340.   mPrintf("\n     User Status: %10s           ANSI Headers: %3s",
  1341.   (expert) ? "Expert" : "Novice", (logBuf.lbflags.ANSI) ? "Yes" : "No");
  1342.   mPrintf("\n     Floor Mode : %10s           Use Alt .RE : %3s",
  1343.   (FloorMode) ? "On" : "Off",   (logBuf.lbflags.ALT_RE) ? "Yes" : "No");
  1344.   mPrintf("\n Linefeed Needed: %10s           Nulls       : %3d",
  1345.   (termLF) ? "Yes" : "No",  termNulls);
  1346.   mPrintf("\n Print msg time : %10s           Screen width: %3d",
  1347.   (sendTime) ? "Yes" : "No", termWidth);
  1348.   mPrintf("\n Last old msg   : %10s           Half-Duplex : %3s",
  1349.   (oldToo) ? "Yes" : "No", (HalfDup) ? "Yes" : "No");
  1350.   mPrintf("\n Char. delay    : %10d           Room Prompt : %3s",
  1351.   logBuf.lbdelay, (logBuf.lbflags.NoPrompt) ? "No" : "Yes");
  1352.   mPrintf("\n Message Pause  : %10s", (logBuf.lbflags.MSG_PAUSE) ? "Yes": "No");
  1353.   if (cfg.BoolFlags.netParticipant &&
  1354.      (address = SearchList(&MailForward, logBuf.lbname)) != NULL)
  1355.     {
  1356.     mPrintf("\n Forward Mail> to %s @%s.", address->Alias,address->System);
  1357.     };
  1358.   if (FindLocalForward(logBuf.lbname) != NULL)
  1359.     {
  1360.     mPrintf("\n Forward Mail> to local account %s.",
  1361.       FindLocalForward(logBuf.lbname));
  1362.     };
  1363.   mPrintf("\n ");
  1364.   }
  1365.   /*
  1366.   * SaveInterrupted()
  1367.   *
  1368.   * This saves an interrupted message.
  1369.   */
  1370.   void SaveInterrupted(MessageBuffer *SomeMsg)
  1371.     {
  1372.     SYS_FILE temp;
  1373.     SYS_FILE save_mess;
  1374.     extern char CCOutFlag;
  1375.     if (cfg.BoolFlags.HoldOnLost)
  1376.       {
  1377.       sPrintf(temp, LCHeld, thisLog);
  1378.       makeSysName(save_mess, temp, &cfg.holdArea);
  1379.       if (access(save_mess, 0) == -1)
  1380.         {
  1381.         if ((upfd = safeopen(save_mess, WRITE_ANY)) == NULL)
  1382.         printf("Failed to open save file!\n");
  1383.         else
  1384.           {
  1385.           crypte(SomeMsg, STATIC_MSG_SIZE, thisLog);
  1386.           fwrite(SomeMsg, STATIC_MSG_SIZE, 1, upfd);
  1387.           crypte(SomeMsg->mbtext, MAXTEXT, thisLog);
  1388.           fwrite(SomeMsg->mbtext, MAXTEXT, 1, upfd);
  1389.           CCOutFlag = TEXTFILE;
  1390.           RunList(&SomeMsg->mbCC, DisplayCC);
  1391.           fclose(upfd);
  1392.  
  1393.           }
  1394.  
  1395.         }
  1396.  
  1397.       }
  1398.  
  1399.     }
  1400.   /*
  1401.   * TranFiles()
  1402.   *
  1403.   * This handles transfer of files to users:
  1404.   *
  1405.   * 1. Gets number of files, number of bytes.
  1406.   * 2. Performs time calculations.
  1407.   * 3. Starts up protocols.
  1408.   */
  1409.   void TranFiles(int protocol, char *phrase)
  1410.     {
  1411.     extern unsigned long netBytes;
  1412.     int NumFiles;
  1413.     char FileSpec[100];
  1414.     getNormStr("EFILEN", FileSpec, sizeof FileSpec, 0);
  1415.     if (strLen(FileSpec) == 0) return;
  1416.     netBytes = 0l;
  1417.     NumFiles = wildCard(getSize, FileSpec, TRUE, phrase, TRUE);
  1418.     if (NumFiles <= 0l)
  1419.       {
  1420.       mPrintf("Sorry, no match for '%s'.\n ", FileSpec);
  1421.       return;
  1422.  
  1423.       }
  1424.     if (!TranAdmin(protocol, NumFiles)) return;
  1425.     TranSend(protocol, transmitFile, FileSpec, phrase, TRUE);
  1426.  
  1427.     }
  1428.   /*
  1429.   * TranAdmin()
  1430.   *
  1431.   * Transfer file administrator.
  1432.   */
  1433.   char TranAdmin(int protocol, int NumFiles)
  1434.     {
  1435.     long seconds;
  1436.     extern unsigned long netBytes;
  1437.     int s;
  1438.     if (NumFiles != 1 && ((InternalProtocol(protocol) &&
  1439.     !(Table[protocol].flags & IS_NUMEROUS)) ||
  1440.     (!InternalProtocol(protocol) && !DoesNumerous(protocol))))
  1441.       {
  1442.       mPrintf("%s does not support batch mode.\n ",
  1443.       (InternalProtocol(protocol)) ?
  1444.       Table[protocol].name : FindProtoName(protocol));
  1445.       return FALSE;
  1446.  
  1447.       }
  1448.     if (!InternalProtocol(protocol) || Table[protocol].flags & RIGAMAROLE)
  1449.       {
  1450.       mPrintf("This %s transfer involves %s bytes (",
  1451.       (!InternalProtocol(protocol)) ? FindProtoName(protocol) :
  1452.       Table[protocol].name,
  1453.       PrintPretty(netBytes, msgBuf.mbtext));
  1454.       mPrintf("%d file%s", NumFiles, NumFiles == 1 ? "" : "s");
  1455.       if (InternalProtocol(protocol))
  1456.       mPrintf(" : %ld blocks",
  1457.       ((netBytes+(Table[protocol].BlockSize-1))/Table[protocol].BlockSize));
  1458.       mPrintf(").  ");
  1459.       s = (!InternalProtocol(protocol)) ? 1 : protocol;
  1460.       if (Table[s].KludgeFactor != 0 && byteRate != 0)
  1461.         {
  1462.         seconds = (long) Table[s].KludgeFactor * netBytes /
  1463.         (byteRate * 10);
  1464.         if (!CheckDLimit(seconds)) return FALSE;
  1465.         mPrintf("It should take %ld:%02ld.\n ", seconds/60, seconds % 60);
  1466.  
  1467.         }
  1468.       return getYesNo("RDYBEG");
  1469.  
  1470.       }
  1471.     return TRUE;
  1472.  
  1473.     }
  1474. /*
  1475.  * TranSend()
  1476.  *
  1477.  * This does the send work of TranFiles().
  1478.  */
  1479. void TranSend(int protocol, void (*fn)(DirEntry *f), char *FileSpec,
  1480.                                                  char *phrase, char NeedToMove)
  1481. {
  1482.     char success;
  1483.     DirEntry temp = { "", "", 0l };
  1484.     SpecialMessage("Status:File Transfer");
  1485.  
  1486.     startTimer(WORK_TIMER);
  1487.  
  1488.     if (InternalProtocol(protocol)) {
  1489.         TransProtocol = protocol;
  1490.  
  1491.         wildCard((FormatFlag) ? doFormatted : fn, FileSpec, NeedToMove,
  1492.                                         phrase, TRUE);
  1493.  
  1494.         if (Table[protocol].flags & NEEDS_FIN)
  1495.             (*fn)(&temp);
  1496.     }
  1497.     else {
  1498.         success = ExternalProtocol(protocol, FALSE, FileSpec, phrase, NeedToMove);
  1499.         if (success == TRAN_SUCCESS)
  1500.             FileTransStat = FL_SUCCESS;
  1501.         else
  1502.             FileTransStat = FL_FAIL;
  1503.     }
  1504.  
  1505.     if (!InternalProtocol(protocol) || (Table[protocol].flags & IS_DL))
  1506.         *DL_Total += chkTimeSince(WORK_TIMER);
  1507.  
  1508.     TransProtocol = ASCII;
  1509.     if (!InternalProtocol(protocol) || (Table[protocol].flags & RIGAMAROLE))
  1510.         oChar(BELL);
  1511. }
  1512.  
  1513.  
  1514.   /*
  1515.   * transmitFile()
  1516.   *
  1517.   * This dumps a host file with no formatting.
  1518.   */
  1519.   void transmitFile(DirEntry *file)
  1520.     {
  1521.     FILE *fbuf;
  1522.     long fileSize = 0l;
  1523.     char *filename, success;
  1524.     extern char *READ_ANY;
  1525.     filename = file->unambig;
  1526.     if (strLen(filename) == 0)          return; /* no filename */
  1527.     fbuf = safeopen(filename, READ_ANY);
  1528.     if ( fbuf == NULL)                  return; /* cannot open */
  1529.     totalBytes(&fileSize, fbuf);
  1530.     if (Table[TransProtocol].flags & RIGAMAROLE)
  1531.        printf("%s: %s (%ld bytes, %ld blocks)\n", Table[TransProtocol].name, filename, fileSize,
  1532.             ((fileSize+(Table[TransProtocol].BlockSize-1))/Table[TransProtocol].BlockSize));
  1533.     fileMessage(FL_START, filename, TRUE, TransProtocol, 0l);
  1534.     if (Transmission(TransProtocol, STARTUP) != TRAN_SUCCESS)
  1535.       {
  1536.       fclose(fbuf);
  1537.       fileMessage(FL_FAIL, filename, TRUE, TransProtocol, fileSize);
  1538.       return ;
  1539.       }
  1540.     if (Table[TransProtocol].flags & NEEDS_HDR)
  1541.       {
  1542.       if (!(*Table[TransProtocol].SendHdr)(fileSize, filename))
  1543.         {
  1544.         fclose(fbuf);
  1545.         fileMessage(FL_FAIL, filename, TRUE, TransProtocol, fileSize);
  1546.         return ;
  1547.         };
  1548.       };
  1549.     SendThatDamnFile(fbuf, Table[TransProtocol].method);
  1550.     success = (Transmission(TransProtocol, FINISH) != TRAN_SUCCESS) ? FL_FAIL : FL_SUCCESS;
  1551.     fileMessage(success, filename, TRUE, TransProtocol, fileSize);
  1552.     if (TransProtocol == ASCII && outFlag != OUTOK)
  1553.       {
  1554.       doCR();
  1555.       outFlag = OUTOK;
  1556.       };
  1557.    }
  1558. /*
  1559.  * SendThatDamnFile()
  1560.  *
  1561.  * This will actually send the file.
  1562.  */
  1563. void SendThatDamnFile(FILE *fbuf, int (*method)(int c))
  1564.   {
  1565.     int c, delay_factor = 0;
  1566.     #ifdef AMIGA
  1567.     int oldc = 0;
  1568.     #endif
  1569.     if( loggedIn )
  1570.       {
  1571.       if( !onConsole && logBuf.lbdelay > 0 )
  1572.         {
  1573.         delay_factor = ( 256 - logBuf.lbdelay );
  1574.         };
  1575.       };
  1576.     while ((c = fgetc(fbuf)) != EOF && (c != CPMEOF || !textDownload))
  1577.       {
  1578.       if (TransProtocol == ASCII) mputChar(c);
  1579.       if (gotCarrier()) if (!(*method)(c))break;
  1580.       if (TransProtocol == ASCII)
  1581.         {
  1582.         if( loggedIn )
  1583.           {
  1584.           if (!onConsole && logBuf.lbdelay > 0 )
  1585.             {
  1586.             if( delay_factor-- <= 0 )
  1587.               {
  1588.               delay_factor = ( 256 - logBuf.lbdelay );
  1589.               MilliSecPause(3);
  1590.               };
  1591.             };
  1592.           };
  1593.         if (mAbort() || (whichIO == MODEM && !gotCarrier())) break;
  1594.         #ifdef AMIGA
  1595.         /* make handling text files more sane for non-Amiga people */
  1596.         if (gotCarrier() && c == '\n' && oldc != '\r')(*method)('\r');
  1597.         #endif
  1598.  
  1599.         }
  1600.       #ifdef AMIGA
  1601.       oldc = c;
  1602.       #endif
  1603.  
  1604.       }
  1605.     fclose(fbuf);
  1606.     textDownload = FALSE;
  1607.  
  1608.     }
  1609.   /*
  1610.   * tutorial()
  1611.   *
  1612.   * This prints file <filename> on the modem & console.
  1613.   * Returns:  TRUE on success else ERROR.
  1614.   */
  1615.   char tutorial(char *filename, char addHelpArea)
  1616.     {
  1617.     /* This includes writeTutorial */
  1618.     FILE     *fbuf;
  1619.     SYS_FILE fn;
  1620.     char     line[MAXWORD];
  1621.     extern char *READ_TEXT;
  1622.     if (addHelpArea)
  1623.     makeSysName(fn, filename, &cfg.homeArea);
  1624.     else
  1625.     strCpy(fn, filename);
  1626.     if ((fbuf = safeopen(fn, READ_TEXT)) == NULL)
  1627.       {
  1628.       mPrintf(NoFileStr, filename);
  1629.       return ERROR;
  1630.  
  1631.       }
  1632.     if (outFlag != IMPERVIOUS) outFlag     = OUTOK;
  1633.     Output_Citadel_Message("HOTHLP", NULL, NULL, NULL);
  1634.     while (fgets(line, MAXWORD, fbuf) && outFlag != OUTSKIP)
  1635.     mPrintf("%s", line);
  1636.     if (!PrintBanner) outFlag     = OUTOK;
  1637.     fclose(fbuf);
  1638.     return TRUE;    /* good as anything */
  1639.  
  1640.     }
  1641.  
  1642. /*
  1643.  * upLoad()
  1644.  *
  1645.  * This enters a file into current directory.
  1646.  */
  1647. void upLoad(int WC, char *file, char NeedToMove)
  1648. {
  1649.     char fileName[MAX_FILENAME - 1];
  1650.     char curdir[100];
  1651.     char successful;
  1652.     long size=0;
  1653.  
  1654.     getcwd(curdir, 100);
  1655.     if (file)
  1656.         strcpy(fileName, file);
  1657.     else
  1658.         getNormStr("EFILEN", fileName, sizeof fileName, 0);
  1659.     if (!fileName[0]) return;
  1660.  
  1661.         /* Can't tolerate bad file names */
  1662.     if (!ValidDirFileName(fileName)) {
  1663.         mPrintf("Illegal file name.\n ");
  1664.         return ;
  1665.     }
  1666.  
  1667.     if (NeedToMove) {
  1668.             if (!setSpace(&roomBuf)) {          /* System error -- yucky. */
  1669.                 return ;
  1670.         }
  1671.     }
  1672.  
  1673.     if (LowFree != 0 && RoomLeft(&roomBuf) < LowFree) {
  1674.         mPrintf("Sorry, not enough room left on disk.\n ");
  1675.         if (NeedToMove)
  1676.             homeSpace();
  1677.         return ;
  1678.     }
  1679.  
  1680.     if (access(fileName, 0) != -1) {
  1681.                                     /* File already exists */
  1682.         mPrintf("\n File: %s already exists.\n ", fileName);
  1683.         if (NeedToMove)
  1684.             homeSpace();
  1685.         return;
  1686.     } else {                    /* Go for it */
  1687.         if (!expert && InternalProtocol(WC)) {
  1688.             homeSpace();
  1689.             tutorial(Table[WC].UpBlbName, TRUE);
  1690.             if (!NeedToMove)
  1691.                 chdir(curdir);
  1692.             else {
  1693.       /****
  1694.                 if (!aide && roomBuf.rbflags.REDIRECT_UPLOADS
  1695.                     && cfg.newupArea.naDirname[0]) {
  1696.                     netSetNewArea(&cfg.newupArea);
  1697.                 }
  1698.                 else
  1699.       *****/
  1700.                     setSpace(&roomBuf);
  1701.             }
  1702.         }
  1703.  
  1704.         if (!getYesNo("RDYBEG")) {
  1705.             if (NeedToMove)
  1706.                 homeSpace();
  1707.             return;
  1708.         }
  1709.  
  1710.         if (!InternalProtocol(WC)) {
  1711.             fileMessage(FL_START, fileName, FALSE, WC, 0l);
  1712.             successful = (ExternalProtocol(WC, TRUE, fileName, NULL, NeedToMove)
  1713.                                                 == TRAN_SUCCESS);
  1714.             if (successful) {       /* so we can get the file size */
  1715.                 upfd = safeopen(fileName, READ_ANY);
  1716.             }
  1717.         }
  1718.         else {
  1719.             if ((upfd = safeopen(fileName, WRITE_ANY)) == NULL) {
  1720.                 mPrintf("\n Can't create %s!\n", fileName);
  1721.                 if (NeedToMove)
  1722.                     homeSpace();
  1723.                 return;
  1724.             }
  1725. #ifdef HORRID_AMIGA_LATTICE_BUG
  1726.             setnbf(upfd);
  1727. #endif
  1728.             fileMessage(FL_START, fileName, FALSE, WC, 0l);
  1729.             successful = (Reception(WC, putFLChar) == TRAN_SUCCESS);
  1730.         }
  1731.  
  1732.         if (successful) totalBytes(&size, upfd);
  1733.         fclose(upfd);
  1734.  
  1735.         if (!successful) unlink(fileName);
  1736.         else if ((successful = FileIntegrity(fileName))==0) {
  1737.             unlink(fileName);
  1738.         }
  1739.  
  1740.         if (NeedToMove)
  1741.             homeSpace();
  1742.  
  1743.         fileMessage(successful ? FL_SUCCESS : FL_FAIL, fileName,
  1744.                                         FALSE, WC, size);
  1745.         if (successful && fileName[0] != '*' && NeedToMove)  {
  1746.   /***          if (loggedIn)
  1747.                 upldcnt++;  ***/
  1748.             FileCommentUpdate(fileName, TRUE);
  1749.         }
  1750.     }
  1751. }
  1752.  
  1753.  
  1754.   /*
  1755.   * FileCommentUpdate()
  1756.   *
  1757.   * This updates the file comment.
  1758.   */
  1759.   void FileCommentUpdate(char *fileName, char aideMsg)
  1760.     {
  1761.     char *tmp;
  1762.     /* MsgEntryType = FILE_ENTRY; */
  1763.     do
  1764.       {
  1765.       msgBuf.mbtext[0] = 0;
  1766.       FindFileComment(fileName);
  1767.       mPrintf("Please enter a description of %s (end with blank line).\n ", fileName);
  1768.       doCR();
  1769.  
  1770.       }
  1771.     while (onLine() && !GetBalance(ASCII, msgBuf.mbtext, MAXTEXT-50));
  1772.     if (onLine())
  1773.       {
  1774.       setSpace(&roomBuf); /* update file comments */
  1775.       while ((tmp = strchr(msgBuf.mbtext, '\r')) != NULL)
  1776.       *tmp = ' ';
  1777.       while ((tmp = strchr(msgBuf.mbtext, '\n')) != NULL)
  1778.       *tmp = ' ';
  1779.       if (aideMsg || strLen(msgBuf.mbtext) != 0)
  1780.         {
  1781.         if (loggedIn && !roomBuf.rbflags.ANON)
  1782.         sPrintf(lbyte(msgBuf.mbtext), " [%s].", logBuf.lbname);
  1783.         updFiletag(fileName, msgBuf.mbtext);
  1784.  
  1785.         }
  1786.       homeSpace();
  1787.       if (aideMsg || strLen(msgBuf.mbtext) != 0)
  1788.         {
  1789.         tmp = strdup(msgBuf.mbtext);
  1790.         ZeroMsgBuffer(&msgBuf);
  1791.         sPrintf(msgBuf.mbtext, "File \"%s\" uploaded into %s.",
  1792.         fileName, formRoom(thisRoom, FALSE, FALSE));
  1793.         if (loggedIn)
  1794.         sPrintf(lbyte(msgBuf.mbtext) - 1, " by %s.", logBuf.lbname);
  1795.         if (aideMsg) aideMessage(NULL,FALSE);
  1796.         sPrintf(msgBuf.mbtext, "File \"%s\" uploaded into %s:\n \n%s",
  1797.         fileName, formRoom(thisRoom, FALSE, FALSE), tmp);
  1798.         strCpy(msgBuf.mbauth, "Citadel");
  1799.         putMessage(&logBuf);  /* Now save message in this room*/
  1800.         noteRoom();
  1801.  
  1802.         }
  1803.       if (tmp != NULL) free(tmp);
  1804.  
  1805.       }
  1806.  
  1807.     }
  1808.   /*
  1809.   * visible()
  1810.   *
  1811.   * This converts given char to printable form if nonprinting.
  1812.   */
  1813.   char visible(AN_UNSIGNED c)
  1814.     {
  1815.     if (c==0xFF)  c = '$' ;   /* start-of-message in message.buf  */
  1816.     c       = c & 0x7F  ;   /* kill high bit otherwise    */
  1817.     if ( c < ' ') c = c + 'A' -1;   /* make all control chars letters   */
  1818.     if (c== 0x7F) c = '~' ;   /* catch DELETE too     */
  1819.     return (char)(c);
  1820.  
  1821.     }
  1822.   char *Menu = NULL;
  1823.   char **ValidMenuOpts;
  1824.   /*
  1825.   * GetMenuChar()
  1826.   *
  1827.   * This will get a character for a menu.
  1828.   */
  1829.   int GetMenuChar()
  1830.     {
  1831.     int c, i;
  1832.     c = toUpper(iChar());
  1833.     for (i = 0; ValidMenuOpts[i][0]; i++)
  1834.     if (c == ValidMenuOpts[i][0])
  1835.     break;
  1836.     if (!ValidMenuOpts[i][0])
  1837.       {
  1838.       if (!onLine() || (expert && c != '?'))
  1839.         {
  1840.         c = 0;
  1841.         mPrintf(" ?\n ");
  1842.  
  1843.         }
  1844.       else
  1845.         {
  1846.         c = '?';
  1847.         if (Menu != NULL) tutorial(Menu, TRUE);
  1848.  
  1849.         }
  1850.  
  1851.       }
  1852.     else mPrintf("%s ", ValidMenuOpts[i] + 1);
  1853.     return c;
  1854.  
  1855.     }
  1856.   /*
  1857.   * ExtraOption()
  1858.   *
  1859.   * This adds an option to a menu.
  1860.   */
  1861.   void ExtraOption(char *Opts[], char *NewOpt)
  1862.     {
  1863.     int i;
  1864.     for (i = 0; Opts[i][0] && Opts[i][0] != ' '; i++)
  1865.     ;
  1866.     if (!Opts[i][0])
  1867.     crashout("INTERNAL: No room for new option!");
  1868.     Opts[i] = NewOpt;
  1869.  
  1870.     }
  1871.   static char *MPtr;
  1872.   static int  MCount;
  1873.   /*
  1874.   * CmdMenuList()
  1875.   *
  1876.   * This reads in a command line, doing backspacing.
  1877.   */
  1878.   int CmdMenuList(char *Opts[], SListBase *Selects, char *HelpFile, char *buf,
  1879.   char moreYet, char OneMore)
  1880.     {
  1881.     int c, rover;
  1882.     char *cmd;
  1883.     void CopyBuf();
  1884.     do
  1885.       {
  1886.       c = toUpper(iChar());
  1887.       if (c == '\b')
  1888.         {
  1889.         mPrintf(" \b");
  1890.         if ((cmd = GetLast(Selects)) == NULL)
  1891.           {
  1892.           if (!OneMore) oChar(' ');
  1893.           return BACKED_OUT;
  1894.  
  1895.           }
  1896.         for (rover = 0; rover < strlen(cmd) - 1; rover++)
  1897.         if (cmd[rover] != '\b') mPrintf("\b \b");
  1898.         else oChar(' ');
  1899.         KillData(Selects, cmd);
  1900.         if (OneMore)
  1901.         mPrintf("\b \b");
  1902.  
  1903.         }
  1904.       else
  1905.         {
  1906.         for (rover = 0; Opts[rover][0] != 0; rover++)
  1907.         if (toUpper(Opts[rover][1]) == toupper(c))
  1908.         break;
  1909.         if (c == 0 || Opts[rover][0] == 0 ||
  1910.         SearchList(Selects, Opts[rover] + 1) != NULL)
  1911.           {
  1912.           if (!onLine() || (expert && c != '?'))
  1913.             {
  1914.             mPrintf(" ?\n ");
  1915.  
  1916.             }
  1917.           else if (HelpFile != NULL)
  1918.             {
  1919.             tutorial(HelpFile, TRUE);
  1920.  
  1921.             }
  1922.           else mPrintf(" ? (Type '?' for menu)\n \n"   );
  1923.           buf[0] = 1;   /* general error */
  1924.           return BAD_SELECT;
  1925.  
  1926.           }
  1927.         mPrintf("%s ", Opts[rover] + 2);
  1928.         AddData(Selects, Opts[rover] + 1, NULL, FALSE);
  1929.  
  1930.         }
  1931.  
  1932.       }
  1933.     while ((c == '\b' || Opts[rover][0] != TERM[0]) && moreYet);
  1934.     MPtr = buf;
  1935.     MCount = 0;
  1936.     RunList(Selects, CopyBuf);
  1937.     return GOOD_SELECT;
  1938.  
  1939.     }
  1940.   void CopyBuf(char *name)
  1941.     {
  1942.     MPtr[MCount++] = name[0];
  1943.     MPtr[MCount] = 0;
  1944.  
  1945.     }
  1946.   /*
  1947.   * FindSelect()
  1948.   *
  1949.   * This function will find an element of a command selection.
  1950.   */
  1951.   void *FindSelect(char *element, char *data)
  1952.     {
  1953.     if (element == data) return element;
  1954.     return NULL;
  1955.  
  1956.     }
  1957.